Win10 UWP 開發系列:使用多語言工具包讓應用支持多語言

来源:http://www.cnblogs.com/yanxiaodi/archive/2016/01/04/5091970.html
-Advertisement-
Play Games

之前我在一篇blog中寫過如何使用多語言工具包,見http://www.cnblogs.com/yanxiaodi/p/3800767.html在WinEcos社區也發佈過一篇詳細的文章介紹多語言工具包的使用,但因社區改版那篇文章已經找不到了。當時寫的時候還沒有出Win10的SDK,都是基於UAP框...


之前我在一篇blog中寫過如何使用多語言工具包,見http://www.cnblogs.com/yanxiaodi/p/3800767.html

在WinEcos社區也發佈過一篇詳細的文章介紹多語言工具包的使用,但因社區改版那篇文章已經找不到了。

當時寫的時候還沒有出Win10的SDK,都是基於UAP框架寫的。微軟早已經發佈了Win10的SDK,相應的項目結構也發生了變化,以前分為兩個項目通過Share項目共用代碼的方式被拋棄,改為合併為一個項目,真正實現了一套代碼相容PC和Mobile兩個平臺,我已經基於Win10 10586的SDK發佈了Currency Exchanger的新版本,下載地址:https://www.microsoft.com/store/apps/9WZDNCRDQ91S  

在開發Currency Exchanger的過程中,我又整理了一下支持多語言的問題,記錄於此。

一、安裝多語言工具包

使用VS2015開發UWP不能再使用老版3.0的多語言工具包,而應該使用新版的V4.0beta,這個還不是正式版,所以相容性有問題,無法與V3.0共存,安裝之後也無法再用VS2013或VS2015打開WP8.1之前的項目,所以安裝之前請慎重!請慎重!請慎重!重要的事情說三遍。

我們在開發者後臺的下載欄目可以找到多語言工具包的下載頁面:https://dev.windows.com/zh-cn/develop/multilingual-app-toolkit

但是!截至到2015年12月31日,這個頁面所下載的中文多語言工具包仍然是V3.0,而不是最新的V4.0beta,就算安裝了,也無法在UWP項目中應用。

最新版的下載地址在此:

https://visualstudiogallery.msdn.microsoft.com/6dab9154-a7e1-46e4-bbfa-18b5e81df520

這也是我一直吐槽MSDN的原因之一,找個東西累死了,官方的東西都不好找。

還有一種方式,直接在VS2015的擴展里搜索Multilingual App Toolkit,主要要有空格,不然搜不到:

要安裝V4.0beta這個。下麵那個是舊版的,這兩個無法共存。

二、啟用多語言工具包

還是做個例子吧。新建一個MultilingualDemo項目,VS2015工具菜單-Multilingual App Toolkit -啟用選定內容

會收到提示:1>  未啟用項目"MultilingualDemo"-沒有可本地化的資源被髮現。 

 這是因為沒有發現咳本地化的資源,雙擊Package.appxmanifest打開,設置一個預設語言,如果在設計的時候就想支持多語言,最好預設語言設置為英語,輸入en-US:

然後在項目中添加一個Strings文件夾,再在其下添加一個en-US文件夾,這個文件夾名字要和預設語言代碼保持一致,如果預設語言是zh-CN,那就建一個zh-CN的文件夾。

在這個目錄下添加一個Resources.resw資源文件,在這裡面編輯所需要的字元串:

添加幾個資源,註意,如果是要顯示在界面上的,可以根據控制項的屬性來設置,如TextBlock的文字是Text屬性,那資源的名字就命名為HelloWorld.Text,Button的文字是Content屬性,所以命名為ClickMe.Content,另外我還加了一個AppName,用於在代碼中使用。

 

再次 VS2015工具菜單-Multilingual App Toolkit -啟用選定內容

 

這次可以正常啟用了:

 

1> 項目"MultilingualDemo"已啟用。 該項目的來源,文化是 'en-US' [英語(美國)]。

 

三、在XAML界面上使用語言資源

在Page中放一個 TextBlock,一個Button,一個ComboBox,設置其x:Uid(資源標識符,註意不是x:Name)屬性,這樣控制項就可以根據資源找到其對應的內容:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"  DataContext="{StaticResource DesignVM}">
        <TextBlock x:Name="pageTitle" Grid.Column="1" Margin="100" Text="{Binding Title}" />
        <TextBlock x:Name="textBlock" x:Uid="HelloWorld" HorizontalAlignment="Left" Margin="100,156,0,0" TextWrapping="Wrap"  VerticalAlignment="Top"/>
        <Button x:Name="button" x:Uid="ClickMe" HorizontalAlignment="Left" Margin="100,181,0,0" VerticalAlignment="Top"/>
        <ComboBox x:Name="comboBox" x:Uid="ChangeLanguage" HorizontalAlignment="Left" Margin="100,249,0,0" VerticalAlignment="Top" Width="120"/>

    </Grid>

 

四、在代碼中使用語言資源

添加一個

    public static class AppResources
    {
        private static ResourceLoader CurrentResourceLoader
        {
            get { return _loader ?? (_loader = ResourceLoader.GetForCurrentView("Resources")); }
        }

        private static ResourceLoader _loader;
        private static readonly Dictionary<string, string> ResourceCache = new Dictionary<string, string>();

        public static string GetString(string key)
        {
            string s;
            if (ResourceCache.TryGetValue(key, out s))
            {
                return s;
            }
            else
            {
                s = CurrentResourceLoader.GetString(key);
                ResourceCache[key] = s;
                return s;
            }
        }



        /// <summary>
        /// AppName
        /// </summary>
        public static string AppName
        {
            get
            {
                return CurrentResourceLoader.GetString("AppName");
            }
        }
    }

 打開MainPage_Model.cs,取消OnBindedViewLoad方法的註釋,在裡面添加以下代碼:

        /// <summary>
        /// This will be invoked by view when the view fires Load event and this viewmodel instance is already in view's ViewModel property
        /// </summary>
        /// <param name="view">View that firing Load event</param>
        /// <returns>Task awaiter</returns>
        protected override Task OnBindedViewLoad(MVVMSidekick.Views.IView view)
        {
            this.Title = AppResources.AppName;
            return base.OnBindedViewLoad(view);
        }

現在運行看看:

現在預設語言是en-US。

五、翻譯成本地化資源

如果沒安裝多語言工具包的話,可以在Strings目錄下手動添加對應語言的文件夾和資源文件,不過有多語言工具包的話這個工作就變得很容易了,在項目上右鍵,添加翻譯語言:

在這裡可以選擇要添加什麼語言,選擇簡體中文:

這裡建議只選擇zh-Hans就可以了,不用選擇zh-CN,因為語言與資源的匹配非常複雜,語言標記存在多種可能影響匹配優先順序的可選組件,建議讓系統來選擇,MSDN文檔是這麼說的:(https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/mt607079.aspx

使用某個語言標記的可選組件的示例有:

  • 用於禁止腳本語言的腳本。例如,en-Latn-US 與 en-US 匹配。
  • 區域。例如,en-US 與 en 匹配。
  • 變體。例如,de-DE-1996 與 de-DE 匹配。
  • -x 和其他擴展名。例如,en-US-x-Pirate 與 en-US 匹配。

 

對於不採用 xx 或 xx-yy 形式的語言標記,也存在許多組件,且並非全部匹配。

  • zh-Hant 與 zh-Hans 不匹配。

 

Windows 以一個標準的易於理解的方式排定語言匹配的優先順序。例如,按優先順序,en-US 依次與 en-US、en、en-GB 等等匹配。

  • Windows 執行跨區域匹配。例如,en-US 與 en-US 匹配,然後依次與 en、en-* 匹配。
  • Windows 提供了一些額外數據,它們可用於區域內(如某種語言的主要區域)的相關性匹配。例如,fr-FR 比 fr-CA 更匹配 fr-BE。
  • 如果你使用 Windows API,則可以免費獲取日後 Windows 在語言匹配方面的任何改進。

在與列表中的首個語言匹配之後才會與列表中第二個語言匹配,對於其他區域變體也是如此。例如,如果應用程式語言為 en-US,則會先於 fr-CA 資源選擇用於 en-GB 的資源。僅當沒有用於 en 形式的資源時才選擇用於 fr-CA 的資源。

應用程式語言列表設置為用戶的區域變體,儘管該變體不同於應用提供的區域變體。例如,如果用戶使用 en-GB,但應用支持 en-US,則應用程式語言列表將包含 en-GB。這將確保日期、時間和數字的格式更接近用戶的期望 (en-GB),但仍然使用應用支持的語言 (en-US) 載入 UI 資源(由於語言匹配)。

除非想把本地化做的非常完善,為中國用戶和新加坡用戶都提供不同的語言資源,否則只提供一種中文簡體就夠了。

選擇後,Strings目錄下會自動添加中文簡體資源文件:

但是中文的資源里還是空的,我們需要翻譯一下英文資源。右鍵單擊MultilingualDemo.zh-Hans.xlf,選擇打開方式:

現在可以輸入翻譯了,如果懶的話就點擊菜單翻譯按鈕調用Bing的翻譯介面自動翻譯一下,保存。

重新編譯生成一下項目,多語言工具包會根據預設資源去填充其他語言的資源文件:

現在可以看到中文的資源文件里也已經有了。翻譯的時候要註意,字元串有幾種狀態,新、翻譯、需要評審、最終等,可以根據這幾種狀態靈活切換顯示哪些字元串來處理。

重新運行項目,如果我們的電腦系統預設是中文語言,那app應該已經變成中文界面了。如果用戶電腦或手機預設語言是英語,則會調用en-US,如果是其他語言,則會調用預設語言en-US。

六、更改首選語言

App應具有可更改語言的設置。為了保存用戶的首選語言,需要使用Windows.Storage.ApplicationData.Current.LocalSettings來保存用戶的設置,關於如何使用這個來保存配置網上有很多介紹,這裡就不詳細介紹了。

在MainPage.xaml裡頭部添加以下命名空間:

xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" 

把ComboBox代碼改成這樣:

        <ComboBox x:Name="comboBox" x:Uid="ChangeLanguage" HorizontalAlignment="Left" Margin="100,249,0,0" VerticalAlignment="Top" Width="120"
                  ItemsSource="{Binding LanguageList}" SelectedIndex="{Binding LanguageCodeIndex, Mode=TwoWay}" >
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding DisplayName}"  />
                </DataTemplate>
            </ComboBox.ItemTemplate>
            <Interactivity:Interaction.Behaviors>
                <Core:EventTriggerBehavior EventName="SelectionChanged">
                    <Core:InvokeCommandAction Command="{Binding CommandLanguageChanged}" CommandParameter="{Binding LanguageCodeIndex}"/>
                </Core:EventTriggerBehavior>
            </Interactivity:Interaction.Behaviors>
        </ComboBox>

 

這裡我們主要使用了Windows.Globalization裡面的類,Windows.Globalization 還具有作為幫助程式對象提供的 Language 對象。它幫助應用檢查有關語言的詳細信息,例如,語言的腳本、顯示名稱和本地名稱。主要語言替代PrimaryLanguageOverride是一個簡單的替代設置,它用於讓用戶獨立選擇語言的應用,或者有充分理由替代預設語言選擇的應用。

相應的vm里添加以下代碼:

public ObservableCollection<Language> LanguageList
        {
            get { return _LanguageListLocator(this).Value; }
            set { _LanguageListLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property ObservableCollection<Language> LanguageList Setup        
        protected Property<ObservableCollection<Language>> _LanguageList = new Property<ObservableCollection<Language>> { LocatorFunc = _LanguageListLocator };
        static Func<BindableBase, ValueContainer<ObservableCollection<Language>>> _LanguageListLocator = RegisterContainerLocator<ObservableCollection<Language>>("LanguageList", model => model.Initialize("LanguageList", ref model._LanguageList, ref _LanguageListLocator, _LanguageListDefaultValueFactory));
        static Func<ObservableCollection<Language>> _LanguageListDefaultValueFactory = () => { return new ObservableCollection<Language>(); };
        #endregion


        /// <summary>
        /// 語言設置
        /// </summary>
        public int LanguageCodeIndex
        {
            get { return _LanguageCodeIndexLocator(this).Value; }
            set { _LanguageCodeIndexLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property int LanguageCodeIndex Setup        
        protected Property<int> _LanguageCodeIndex = new Property<int> { LocatorFunc = _LanguageCodeIndexLocator };
        static Func<BindableBase, ValueContainer<int>> _LanguageCodeIndexLocator = RegisterContainerLocator<int>("LanguageCodeIndex", model => model.Initialize("LanguageCodeIndex", ref model._LanguageCodeIndex, ref _LanguageCodeIndexLocator, _LanguageCodeIndexDefaultValueFactory));
        static Func<int> _LanguageCodeIndexDefaultValueFactory = () => { return -1; };
        #endregion


        public CommandModel<ReactiveCommand, String> CommandLanguageChanged
        {
            get { return _CommandLanguageChangedLocator(this).Value; }
            set { _CommandLanguageChangedLocator(this).SetValueAndTryNotify(value); }
        }
        #region Property CommandModel<ReactiveCommand, String> CommandLanguageChanged Setup        

        protected Property<CommandModel<ReactiveCommand, String>> _CommandLanguageChanged = new Property<CommandModel<ReactiveCommand, String>> { LocatorFunc = _CommandLanguageChangedLocator };
        static Func<BindableBase, ValueContainer<CommandModel<ReactiveCommand, String>>> _CommandLanguageChangedLocator = RegisterContainerLocator<CommandModel<ReactiveCommand, String>>("CommandLanguageChanged", model => model.Initialize("CommandLanguageChanged", ref model._CommandLanguageChanged, ref _CommandLanguageChangedLocator, _CommandLanguageChangedDefaultValueFactory));
        static Func<BindableBase, CommandModel<ReactiveCommand, String>> _CommandLanguageChangedDefaultValueFactory =
            model =>
            {
                var resource = "CommandLanguageChanged";           // Command resource  
                var commandId = "CommandLanguageChanged";
                var vm = CastToCurrentType(model);
                var cmd = new ReactiveCommand(canExecute: true) { ViewModel = model }; //New Command Core

                cmd.DoExecuteUIBusyTask(
                        vm,
                        async e =>
                        {
                            //Todo: Add LanguageChanged logic here, or
                            await MVVMSidekick.Utilities.TaskExHelper.Yield();
                            string oldLan = AppSettings.Instance.LanguageCode;
                            if (vm.LanguageCodeIndex >= 0)
                            {
                                var lan = vm.LanguageList[vm.LanguageCodeIndex];
                                AppSettings.Instance.LanguageCode = lan.LanguageTag;
                                ApplicationLanguages.PrimaryLanguageOverride = lan.LanguageTag;
                            }
                        })
                    .DoNotifyDefaultEventRouter(vm, commandId)
                    .Subscribe()
                    .DisposeWith(vm);

                var cmdmdl = cmd.CreateCommandModel(resource);

                cmdmdl.ListenToIsUIBusy(
                    model: vm,
                    canExecuteWhenBusy: false);
                return cmdmdl;
            };

        #endregion

屬性可以用propvm代碼段,命令用propcmd代碼段來快速生成。

在MainPage的vm的load事件中初始化語言列表:

if (!LanguageList.Any())
            {
                var lanList = ApplicationLanguages.ManifestLanguages;
                foreach (var lan in lanList)
                {
                    LanguageList.Add(new Language(lan));
                }
            }
            if (!string.IsNullOrEmpty(AppSettings.Instance.LanguageCode))
            {
                Language userLan = LanguageList.FirstOrDefault(x => x.LanguageTag == AppSettings.Instance.LanguageCode);
                LanguageCodeIndex = LanguageList.IndexOf(userLan);
            }

如果用戶更改語言後,在程式載入時應該按照用戶選擇的語言來調用,打開App.xaml.cs,在App構造 函數中添加以下代碼:

public App()
        {
            //TODO 這裡可以根據用戶需要更改語言
            if (!string.IsNullOrEmpty(AppSettings.Instance.LanguageCode))
            {
                ApplicationLanguages.PrimaryLanguageOverride = AppSettings.Instance.LanguageCode;
            }
            //ApplicationLanguages.PrimaryLanguageOverride = "cs";
            //ResourceContext.GetForCurrentView().Reset();
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

現在一個具有基本多語言支持、可更改語言的app就完成了。用戶選擇不同的語言後,重新打開就會重新設置語言。

七、其他

這裡是微軟官方的一個例子,不過是win8平臺的,可以參考:https://code.msdn.microsoft.com/windowsapps/Application-resources-and-cd0c6eaa/

還有一些特殊的情況需要考慮,如果非要做的那麼完美的話:

  • 有可能有的語言字元數比較多,導致控制項寬度不夠,這就需要為每種語言設置控制項的寬度,比如創建App_Name.Width的資源;
  • 有可能語言的排列方向不一致,比如阿拉伯語是右對齊,可以設置對齊屬性;
  • 有可能圖像也需要本地化,那就需要按照資源限定符的格式來定義圖片路徑:

    標準命名約定為foldername/qualifiername-value_qualifiername-value/filename.qualifiername-value_qualifiername-value.ext,當資源路徑為Images/en-US/homeregion-USA/logo.scale-100_contrast-white.png時,應以Images/logo.png的方式來載入。

關於如何使用資源限定符,可參考:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/hh965324.aspx

這個頁面是一些本地化的最佳實踐:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/hh967762.aspx

CurrencyExchanger的本地化我也沒有做的那麼完善,基本就是只翻譯了語言,沒有考慮寬度啊排列方式這些,工作量太大了。

 

至於如何翻譯這些文字,建議在app里留一個郵件,讓志願者來幫助翻譯,可以將xlf文件上右鍵導出翻譯,選擇xlf格式或者csv格式,發送給翻譯人員翻譯,然後再導入進來即可。但這裡又會遇到一個問題,導出的csv用excel打開的話,再另存,會丟失裡面的雙引號。

從xlf文件導出的csv格式是這樣的:

除了表頭,每個欄位兩邊都有雙引號。

這個文件用excel打開再另存後,再用文本編輯軟體打開,會變成這樣:

兩邊的引號沒有了,導入的時候會提示錯誤,無法導入。

我想了個笨辦法,在excel里編輯csv的時候,修改單元格屬性,改為   !"@!" 然後再保存,這樣在文本編輯軟體里打開會發現一個雙引號變成了兩個,然後再使用批量替換功能,把"""替換為",同時別忘了把第一行表頭的雙引號去掉,才能正確導入。

其實如果直接把資源文件里的內容複製到excel里發送給翻譯人員翻譯,翻譯好了再粘貼回來也行,但要是以後再增加修改預設語言的資源時,其他語言也得手動挨個改,不如用多語言工具包自動填充完整。各有利弊。

 

最後附上demo下載地址:

鏈接:http://pan.baidu.com/s/1i4jBn8L 密碼:idpz


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • setpoint.html:行政區域工具 用Winform 讀取百度地圖的經緯度:using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Dra....
  • 增加節點時,我們是這樣寫的:xop.Document.Element("messages").Add( new XElement("message", new XAttribute("event", ...
  • 經過十多天的艱苦奮戰,MyKTV點歌系統終於成型,從剛開始接到項目的茫然,到完成項目時的喜悅,整個過程的艱辛和付出只有自己知道.雖然這個項目還有許多需要完善的地方,譬如添加歌詞信息,實現窗體的美化等,這些在後續時間里我再一一進行一個完善吧!首先呢,我先將整個項目所能實現的功能做一個簡單的介紹,K.....
  • 配置代碼: 異常信息: [Error] An unhandled exception was thrown by the application.System.Security.Cryptography.CryptographicException: An error occurred while ...
  • 在web頁面上我們可以通過frameset,iframe嵌套框架很容易實現各種導航+內容的佈局界面,而在winform、WPF中實現其實也很容易,我這裡就分享一個:在winform下實現左右佈局多視窗界面。我這裡說的多視窗是指一個父視窗包含多個子視窗,在winform中實現這種效果很簡單,即將某個窗...
  • 最近對UWP開發比較感興趣,大概瞭解之後覺得有必要回顧一下xaml相關內容。。於是抽時間把以前學WPF的相關材料搜羅了一下順便和新的xaml特性做了一個小總結。。希望能堅持寫下去吧。。O(∩_∩)O~ 個人覺得還是從比較零散的xaml佈局開始寫比較合適,這部分直接決定了應用程式的實用性,尤其是在出....
  • 繼上次的前臺內容今天我們來講一講後臺的一些事兒吧!後臺呢主要是對前臺的歌手、歌曲信息及圖片、歌曲路徑的一些~嗯~算是配置吧! 還是先看效果圖 後臺效果圖 登錄界面 主界面 添加歌手 該窗體填寫歌手信息並選擇圖片後會將歌手的信息保存在資料庫並且將選中的圖片複製到資料庫中存放歌手圖像的制定路徑中! 查詢...
  • FrameSet框架集
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...