Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ sync'd Oct. 17, 2025
| PWA Background Sync | Lets you send chat messages even when offline. If you're offline when sending a message, the app uses Background Sync to send the message later, when you're back online. | [/pwa-background-sync/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-background-sync/) | [PWA Background Sync API demo](https://microsoftedge.github.io/Demos/pwa-background-sync/) |
| PWA file handlers | Handles `*.txt` files like a native application does. | [/pwa-file-handlers/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-file-handlers/) | [PWA file handlers demo](https://microsoftedge.github.io/Demos/pwa-file-handlers/) |
| PWA installer | A PWA that uses the Web Install API to install other PWAs. Also uses CSS Masonry. | [/pwa-installer/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-installer/) | [pwa-installer](https://microsoftedge.github.io/Demos/pwa-installer/) demo |
| Manifest Localization | A PWA that demonstrates localized app metadata in the web app manifest, supporting English, German, and Arabic. | [/pwa-manifest-localization/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-manifest-localization/) | [Manifest Localization demo](https://microsoftedge.github.io/Demos/pwa-manifest-localization/) |
| Edge demos (pwastore) | A PWA that uses the Web Install API to install other PWAs. Also uses CSS Masonry. An earlier copy of `/pwa-installer/` directory, pointed to by Dev Trial docs. | [/pwa-pwastore/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-pwastore/) | [Edge demos](https://microsoftedge.github.io/Demos/pwa-pwastore/) |
| Timer PWA | Has a **Set timer** button, and you can set the duration of the timer. | [/pwa-timer/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-timer/) | [Timer PWA demo](https://microsoftedge.github.io/Demos/pwa-timer/) |
| PWA To Do | Create lists of tasks locally in your browser, or by installing the app. Click **About** link in rendered demo. | [/pwa-to-do/](https://github.com/MicrosoftEdge/Demos/tree/main/pwa-to-do/) | [PWA To Do](https://microsoftedge.github.io/Demos/pwa-to-do/) demo |
Expand Down
30 changes: 30 additions & 0 deletions pwa-manifest-localization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Manifest Localization for Web Apps

➡️ **[Open the demo](https://microsoftedge.github.io/Demos/pwa-manifest-localization/)** ⬅️

This demo showcases the use of `_localized` suffixed web app manifest members, which allow Progressive Web Apps (PWAs) to provide localized app metadata (such as name, description, and icons) for different languages directly in the manifest file.

The demo app is localized in English, German, and Arabic.

## How to use the demo app

1. In Microsoft Edge, open the [Manifest Localization Test App](https://microsoftedge.github.io/Demos/pwa-manifest-localization/) in a new window or tab.

2. In the address bar, click the **App available** button to install the PWA on your device.

If your browser is set to English, German, or Arabic, the installed PWA displays the name, short name, icon, description, and shortcuts that are defined in the web app manifest file for that language.

3. In Microsoft Edge, go to `edge://settings/languages`.

4. Under **Preferred languages**, click **...** next to either English, German, or Arabic, choosing the one which your browser isn't already using.

5. Select **Display Microsoft Edge in this language**.

6. Restart the PWA.

You are prompted to update the installed PWA to the new localized values.
Comment thread
captainbrosset marked this conversation as resolved.

## Links

- [Specification](https://www.w3.org/TR/appmanifest/#x_localized-members)
- [Chrome Platform Status entry](https://chromestatus.com/feature/5090807862394880?gate=4864426712891392)
Binary file added pwa-manifest-localization/icons/icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa-manifest-localization/icons/icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa-manifest-localization/icons/icon-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pwa-manifest-localization/icons/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions pwa-manifest-localization/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="A Progressive Web App for testing manifest localization capabilities">
<meta name="theme-color" content="#2196f3">
<title>Manifest Localization Test App</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" type="image/png" sizes="128x128" href="icons/icon-128.png">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>Manifest Localization Test App</h1>
<p class="subtitle">Testing PWA manifest localization capabilities</p>
</header>

<div class="content">
<div class="section">
<div class="info-box">
<p>
This PWA demonstrates manifest localization using the <code>name_localized</code>,
<code>short_name_localized</code>, <code>icons_localized</code>,
<code>description_localized</code>, and <code>shortcuts_localized</code> fields.
The service worker caches the manifest for offline access.
</p>
</div>
<div id="sw-status" class="status">Checking Service Worker...</div>
</div>

<div class="section">
<h2>Localized App Names and Icons</h2>
<table>
<thead>
<tr>
<th>Language</th>
<th>App Name</th>
<th>Short Name</th>
<th>Icon</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="language-code">Default (en)</span></td>
<td>Manifest Localization Test App</td>
<td>Localization Test</td>
<td>
<img src="./icons/icon-128.png" alt="Default icon" width="48" height="48">
</td>
</tr>
<tr>
<td><span class="language-code">German (de)</span></td>
<td>Manifest-Lokalisierungstest-App</td>
<td>Lokalisierungstest</td>
<td>
<img src="./icons/localized_icons/de/icon-128.png" alt="German icon" width="48" height="48">
</td>
</tr>
<tr>
<td><span class="language-code">Arabic (ar)</span></td>
<td>تطبيق اختبار توطين البيان</td>
<td>اختبار التوطين</td>
<td>
<img src="./icons/localized_icons/ar/icon-128.png" alt="Arabic icon" width="48" height="48">
</td>
</tr>
</tbody>
</table>
</div>


</div>
</div>

<script src="main.js"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions pwa-manifest-localization/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Register service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service-worker.js')
.then((registration) => {
console.log('Service Worker registered successfully:', registration.scope);
updateStatus('Service Worker Active ✓', true);
})
.catch((error) => {
console.error('Service Worker registration failed:', error);
updateStatus('Service Worker Failed ✗', false);
});
});

// Update status when service worker state changes
navigator.serviceWorker.ready.then(() => {
updateStatus('Service Worker Active ✓', true);
});
} else {
updateStatus('Service Worker Not Supported ✗', false);
}

function updateStatus(message, isActive) {
const statusEl = document.getElementById('sw-status');
statusEl.textContent = message;
statusEl.className = isActive ? 'status' : 'status offline';
}
116 changes: 116 additions & 0 deletions pwa-manifest-localization/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
"name": "Manifest Localization Test App",
"short_name": "Localization Test",
"description": "A Progressive Web App for testing manifest localization capabilities",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2196f3",
"icons": [
{
"src": "./icons/icon-128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "./icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./icons/icon-256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "./icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"shortcuts": [
{
"name": "Open Home",
"short_name": "Home",
"description": "Navigate to home page",
"url": "./",
"icons": [
{
"src": "./icons/icon-128.png",
"sizes": "128x128",
"type": "image/png"
}
]
}
],
"name_localized": {
"de": "Manifest-Lokalisierungstest-App",
"ar": "تطبيق اختبار توطين البيان"
},
"short_name_localized": {
"de": "Lokalisierungstest",
"ar": "اختبار التوطين"
},
"description_localized": {
"de": "Eine Progressive Web App zum Testen der Manifest-Lokalisierungsfunktionen",
"ar": "تطبيق ويب تقدمي لاختبار قدرات توطين البيان"
},
"icons_localized": {
"de": [
{
"src": "./icons/localized_icons/de/icon-128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "./icons/localized_icons/de/icon-256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"ar": [
{
"src": "./icons/localized_icons/ar/icon-128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "./icons/localized_icons/ar/icon-256.png",
"sizes": "256x256",
"type": "image/png"
}
]
},
"shortcuts_localized": {
"de": [
{
"name": "Startseite öffnen",
"short_name": "Startseite",
"description": "Zur Startseite navigieren",
"url": "./",
"icons": [
{
"src": "./icons/localized_icons/de/icon-128.png",
"sizes": "128x128",
"type": "image/png"
}
]
}
],
"ar": [
{
"name": "فتح الصفحة الرئيسية",
"short_name": "الرئيسية",
"description": "الانتقال إلى الصفحة الرئيسية",
"url": "./",
"icons": [
{
"src": "./icons/localized_icons/ar/icon-128.png",
"sizes": "128x128",
"type": "image/png"
}
]
}
]
}
}
87 changes: 87 additions & 0 deletions pwa-manifest-localization/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const CACHE_NAME = 'manifest-localization-test-v2';
const MANIFEST_URL = './manifest.json';

// Files to cache
const urlsToCache = [
'./',
'./index.html',
'./manifest.json',
'./style.css',
'./main.js',
'./icons/icon-128.png',
'./icons/icon-192.png',
'./icons/icon-256.png',
'./icons/icon-512.png',
'./icons/localized_icons/ar/icon-128.png',
'./icons/localized_icons/ar/icon-256.png',
'./icons/localized_icons/de/icon-128.png',
'./icons/localized_icons/de/icon-256.png'
];

// Install event - cache files
self.addEventListener('install', (event) => {
console.log('Service Worker: Installing...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Service Worker: Caching files');
return cache.addAll(urlsToCache);
})
.then(() => self.skipWaiting())
);
});

// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('Service Worker: Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cache) => {
if (cache !== CACHE_NAME) {
console.log('Service Worker: Clearing old cache');
return caches.delete(cache);
}
})
);
}).then(() => self.clients.claim())
);
});

// Fetch event - serve from cache
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);

// Special handling for manifest.json
if (url.pathname.endsWith('/manifest.json')) {
event.respondWith(
caches.match(event.request)
.then((cachedResponse) => {
if (cachedResponse) {
console.log('Service Worker: Serving cached manifest');
return cachedResponse;
}

// If not in cache, fetch and cache it
return fetch(event.request)
.then((response) => {
const responseClone = response.clone();
caches.open(CACHE_NAME)
.then((cache) => {
cache.put(event.request, responseClone);
});
return response;
});
})
);
return;
}

// Default fetch strategy - cache first, falling back to network
event.respondWith(
caches.match(event.request)
.then((response) => {
return response || fetch(event.request);
})
);
});
Loading