Hello.

I am Paul Kinlan.

A Developer Advocate for Chrome and the Open Web at Google.

testing-file-share-target

Paul Kinlan

Ini adalah pengujian API Target Saham di Android dan kemampuannya berbagi file. Jika Anda melihat sesuatu di sini, maka semuanya baik-baik saja :)

Read More

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium

Ricky Mondello: Adoption of Well-Known URL for Changing Passwords

Paul Kinlan

Ricky Mondello di tim Safari baru-baru ini membagikan catatan tentang bagaimana Twitter menggunakan spesifikasi ./well-known/change-password.

I just noticed that Twitter has adopted the Well-Known URL for Changing Passwords! Is anyone aware of other sites that have adopted it?

Twitter’s implementation: https://twitter.com/.well-known/change-password; Github’s: https://github.com/.well-known/change-password; Specification :https://github.com/WICG/change-password-url

Read full post .

Fitur ini benar-benar melewati saya, tetapi ini adalah ide yang rapi: diberikan file di lokasi yang terkenal, dapatkah browser menawarkan UI kepada pengguna yang memungkinkan mereka untuk dengan cepat mengatur ulang kata sandi mereka tanpa harus menavigasi situs UI yang kompleks ..

Spesifikasinya tampak sederhana: file terkenal hanya berisi URL untuk mengarahkan pengguna ketika mereka ingin melakukan tindakan. Ini membuat saya berpikir, dapatkah kami menawarkan lebih banyak fitur ini:

  • Lokasi terkenal untuk model persetujuan berbasis GDPR (izin cookie) - pemilik situs dapat menawarkan tautan ke halaman tempat pengguna dapat mengelola dan berpotensi mencabut semua cookie dan item persetujuan data lainnya.
  • Lokasi yang terkenal untuk manajemen izin browser - pemilik situs dapat menawarkan tempat cepat bagi pengguna untuk dapat mencabut izin untuk hal-hal seperti lokasi geografis, pemberitahuan dan primitif lainnya.
  • Jalur yang terkenal untuk penghapusan dan perubahan akun
  • Jalur yang terkenal untuk manajemen berlangganan milis

Daftar ini terus berjalan …. Saya sangat menyukai ide untuk file pengalihan sederhana untuk membantu pengguna menemukan tindakan umum pengguna, dan untuk cara browser memunculkannya.

pinch-zoom-element

Paul Kinlan

Jake dan tim membangun elemen kustom yang agak luar biasa ini untuk mengelola pinch zooming pada set HTML apa pun di luar dinamika pinch-zoom browser sendiri (pikirkan zoom mobile viewport). Elemen itu adalah salah satu komponen utama yang kami butuhkan untuk aplikasi squoosh yang kami buat dan rilis di Chrome Dev Summit (… Saya katakan 'dirilis di Chrome Dev Summit' - Jake menunjukkannya kepada semua orang di China Google Developer Day. meskipun anggota tim lainnya berada di bawah embargo;) …)

install: npm install --save-dev pinch-zoom-element

<pinch-zoom>
  <h1>Hello!</h1>
</pinch-zoom>

Read full post .

Saya baru saja menambahkannya ke blog saya (hanya butuh beberapa menit), Anda dapat memeriksanya di bagian ' life ' tempat saya berbagi foto yang telah saya ambil. Jika Anda menggunakan perangkat yang mendukung sentuhan, Anda dapat dengan cepat mencubit-zoom pada elemen, jika Anda menggunakan track-pad yang dapat menangani beberapa input jari yang berfungsi juga.

Elemen ini adalah contoh yang bagus mengapa saya suka komponen web sebagai model untuk membuat komponen antarmuka pengguna. Elemen pinch-zoom tepat di bawah 3kb pada kabel (tidak terkompresi) dan dependensi minimal untuk membangun dan itu hanya melakukan satu pekerjaan dengan sangat baik, tanpa mengikat setiap logika tingkat aplikasi kustom yang akan membuatnya sulit untuk digunakan (saya punya beberapa pemikiran tentang logika UI vs komponen logika aplikasi yang akan saya bagikan berdasarkan pembelajaran saya dari aplikasi Squoosh).

Saya akan senang melihat elemen seperti ini mendapatkan lebih banyak kesadaran dan penggunaan, misalnya saya bisa membayangkan bahwa elemen ini dapat menggantikan atau membakukan fungsi zoom gambar yang Anda lihat di banyak situs perdagangan dan selamanya menghilangkan rasa sakit dari pengembang.

Registering as a Share Target with the Web Share Target API

Paul Kinlan

Pete LePage memperkenalkan API Target Berbagi Web dan ketersediaan di Chrome melalui uji coba asal

Until now, only native apps could register as a share target. The Web Share Target API allows installed web apps to register with the underlying OS as a share target to receive shared content from either the Web Share API or system events, like the OS-level share button.

Read full post .

API ini adalah pengubah permainan di web, membuka web hingga sesuatu yang hanya pernah tersedia untuk aplikasi asli: Berbagi Asli. Aplikasi adalah silo, mereka menyedot semua data dan membuatnya sulit diakses di seluruh platform. Target Share mulai meratakan lapangan bermain sehingga web dapat bermain di game yang sama.

Pengalaman Twitter Mobile memiliki Target Target already enabled . Posting ini dibuat dengan menggunakan Target Target yang saya tentukan di manifest.json panel admin 'situs saya - itu bekerja dengan cukup baik, dan begitu mereka mendapatkan dukungan file saya akan dapat memposting gambar atau gumpalan pada perangkat saya ke blog saya.

Waktu yang sangat menyenangkan.

Baca pos tertaut untuk mempelajari lebih lanjut tentang garis waktu kapan API ini ditayangkan dan bagaimana menggunakan API.

Why Build Progressive Web Apps: Push, but Don't be Pushy! Video Write-Up

Paul Kinlan

Artikel, video, dan contoh hebat oleh Thomas Steiner tentang pemberitahuan push yang bagus di web.

A particularly bad practice is to pop up the permission dialog on page load, without any context at all. Several high traffic sites have been caught doing this. To subscribe people to push notifications, you use the the PushManager interface. Now to be fair, this does not allow the developer to specify the context or the to-be-expected frequency of notifications. So where does this leave us?

Read full post .

Web Push adalah API yang luar biasa kuat, tetapi mudah untuk menyalahgunakan dan mengganggu pengguna Anda. Hal buruk untuk situs Anda adalah jika pengguna memblokir pemberitahuan karena Anda meminta tanpa peringatan, maka Anda tidak mendapatkan kesempatan untuk bertanya lagi.

Perlakukan pengguna Anda dengan hormat, Konteks adalah raja untuk pemberitahuan Push Web.

Maybe Our Documentation "Best Practices" Aren''t Really Best Practices

Paul Kinlan

Kayce Basques, seorang penulis teknologi yang luar biasa di tim kami menulis artikel yang cukup menakjubkan tentang pengalamannya mengukur seberapa baik praktik terbaik dokumentasi yang ada untuk menjelaskan materi teknis. Praktik terbaik dalam hal ini bisa menjadi standar industri yang terkenal untuk penulisan teknis, atau bisa juga panduan gaya penulisan perusahaan Anda sendiri. Coba lihat!

Recently I discovered that a supposed documentation “best practice” may not actually stand up to scrutiny when measured in the wild. I’m now on a mission to get a “was this page helpful?” feedback widget on every documentation page on the web. It’s not the end-all be-all solution, but it’s a start towards a more rigorous understanding of what actually makes our docs more helpful.

Read full post .

Sementara saya bukan seorang penulis teknologi, peran saya melibatkan sejumlah besar keterlibatan dengan tim penulis teknologi kami dan juga menerbitkan banyak 'praktik terbaik' untuk pengembang sendiri. Saya kagum dengan seberapa dalam dan penelitian Kayce telah dilakukan pada seni menulis dokumen modern melalui lensa konten tim kami. Saya sepenuhnya mendorong Anda untuk membaca artikel Kayce secara mendalam - saya belajar banyak. Kayce terima kasih!

Grep your git commit log

Paul Kinlan

Finding code that was changed in a commit

Read More

Performance and Resilience: Stress-Testing Third Parties by CSS Wizardry

Paul Kinlan

Saya berada di China beberapa minggu yang lalu untuk Hari Pengembang Google dan saya menunjukkan kepada semua orang [QRCode scanner] saya (0), itu berfungsi dengan baik sampai saya offline. Ketika pengguna offline (atau sebagian terhubung) kamera tidak akan mulai, yang berarti Anda tidak bisa mengambil kode QR. Saya butuh waktu lama untuk mengetahui apa yang terjadi, dan ternyata saya keliru memulai kamera di acara onload dan permintaan Google Analytics akan hang dan tidak selesai tepat waktu. Itu ini berkomitmen yang memperbaikinya.

Because these types of assets block rendering, the browser will not paint anything to the screen until they have been downloaded (and executed/parsed). If the service that provides the file is offline, then that’s a lot of time that the browser has to spend trying to access the file, and during that period the user is left potentially looking at a blank screen. After a certain period has elapsed, the browser will eventually timeout and display the page without the asset(s) in question. How long is that certain period of time?

It’s 1 minute and 20 seconds.

If you have any render-blocking, critical, third party assets hosted on an external domain, you run the risk of showing users a blank page for 1.3 minutes.

Below, you’ll see the DOMContentLoaded and Load events on a site that has a render-blocking script hosted elsewhere. The browser was completely held up for 78 seconds, showing nothing at all until it ended up timing out.

Baca pos lengkap.

Saya mendorong Anda untuk membaca pos karena ada banyak wawasan luar biasa.

Chrome Bug 897727 - MediaRecorder using Canvas.captureStream() fails for large canvas elements on Android

Paul Kinlan

Pada akhir pekan saya bermain-main dengan pembuat enkoder video Boomerang, Anda dapat membuatnya bekerja hampir secara langsung (akan saya jelaskan nanti). Saya membuatnya bekerja di Chrome di Desktop, tetapi tidak akan berfungsi dengan baik di Chrome pada Android. Lihat kode di sini.

Sepertinya ketika Anda menggunakan captureStream () pada <canvas>yang memiliki resolusi yang relatif besar (1280x720 dalam kasus saya) API MediaRecorder tidak akan dapat menyandikan video dan itu tidak akan salah dan Anda tidak dapat mendeteksi bahwa itu tidak dapat menyandikan video sebelumnya.

(1) Capture a large res video (from getUM 1280x720) to a buffer for later processing. (2) Create a MediaRecorder with a stream from a canvas element (via captureStream) sized to 1280x720 (3) For each frame captured putImageData on the canvas (4) For each frame call canvasTrack.requestFrame() at 60fps

context.putImageData(frame, 0, 0); canvasStreamTrack.requestFrame();

Demo: https://boomerang-video-chrome-on-android-bug.glitch.me/ Code: https://glitch.com/edit/#!/boomerang-video-chrome-on-android-bug?path=script.js:21:42

What is the expected result?

For the exact demo, I buffer the frames and then reverse them so you would see the video play forwards and backwards (it works on desktop). In generall I would expect all frames sent to the canvas to be processed by the MediaRecorder API - yet they are not.

What happens instead?

It only captures the stream from the canvas for a partial part of the video and then stops. It’s not predicatable where it will stop.

I suspect there is a limit with the MediaRecorder API and what resolution it can encode depending on the device, and there is no way to know about these limits ahead of time.

As far as I can tell this has never worked on Android. If you use https://boomerang-video-chrome-on-android-bug.glitch.me which has a 640x480 video frame it records just fine. The demo works at higher-resolution just fine on desktop.

Baca pos lengkap.

Jika Anda ingin bermain-main dengan demo yang berfungsi pada keduanya, maka klik di sini

Why Microsoft and Google love progressive web apps | Computerworld

Paul Kinlan

Posting yang bagus tentang PWA dari Mike Elgan. Saya tidak yakin tentang tujuan Microsoft dengan PWA, tapi saya pikir kami cukup sederhana: kami ingin pengguna memiliki akses ke konten dan fungsi secara instan dan dengan cara yang mereka harapkan dapat berinteraksi dengannya di perangkat mereka. Web harus menjangkau semua orang di setiap perangkat yang terhubung dan pengguna harus dapat mengakses modalitas yang mereka sukai, sebagai aplikasi jika itu yang mereka harapkan (seluler, mungkin), atau suara pada asisten, dll.

Kami masih jauh dari web tanpa kepala, namun, satu hal yang benar-benar mengejutkan saya di artikel:

Another downside is that PWAs are highly isolated. So it’s hard and unlikely for different PWAs to share resources or data directly.

Baca pos lengkap.

Situs dan aplikasi di web tidak seharusnya diisolasi, web adalah dapat ditautkan, diindeks, sementara, tetapi kami semakin terdiam dengan setiap situs yang kami buat. Kami menciptakan silo yang tidak diinginkan karena platform tidak mudah memungkinkan pengguna untuk mendapatkan * data * mereka keluar masuk dengan mudah. Saya tidak berbicara tentang RDF atau sesuatu seperti itu, operasi dasar seperti salin dan tempel, seret dan lepas, bagikan ke situs dan bagikan dari situs rusak di web hari ini, dan itu sebelum kita sampai ke IPC antar bingkai, pekerja dan jendela.

Building a video editor on the web. Part 0.1 - Screencast

Paul Kinlan

Anda harus dapat membuat dan mengedit video hanya dengan menggunakan web di browser. Seharusnya mungkin untuk menyediakan antarmuka pengguna yang mirip dengan Screenflow yang memungkinkan Anda membuat video output yang menggabungkan beberapa video, gambar, dan audio ke dalam satu video yang dapat diunggah ke layanan seperti YouTube. Berikut dari saya posting sebelumnya yang secara singkat menjelaskan persyaratan editor video, dalam posting ini saya hanya ingin cepat menunjukkan dalam screencast bagaimana saya membangun perekam web cam, dan juga bagaimana cara membangun screencast perekam :)

Read More

894556 - Multiple video tracks in a MediaStream are not reflected on the videoTracks object on the video element

Paul Kinlan

Masalah pertama yang saya temukan mencoba membangun editor video di web.

Saya memiliki beberapa aliran video (desktop dan web cam) dan saya ingin dapat beralih antara aliran video pada satu elemen video sehingga saya dapat dengan cepat beralih antara web cam dan desktop dan tidak melanggar MediaRecorder.

Sepertinya Anda harus dapat melakukannya melalui toggling properti selected pada objekvideoTracks pada <video>elemen, tetapi Anda tidak bisa, array trek hanya berisi 1 elemen (trek video pertama di MediaStream).

What steps will reproduce the problem? (1) Get two MediaStreams with video tracks (2) Add them to a new MediaStream and attach as srcObject on a videoElement (3) Check the videoElement.videoTracks object and see there is only one track

Demo at https://multiple-tracks-bug.glitch.me/

What is the expected result? I would expect videoElement.videoTracks to have two elements.

What happens instead? It only has the first videoTrack that was added to the MediaStream.

Baca pos lengkap.

Repro case.

window.onload = () => {
  if('getDisplayMedia' in navigator) warning.style.display = 'none';

  let blobs;
  let blob;
  let rec;
  let stream;
  let webcamStream;
  let desktopStream;

  captureBtn.onclick = async () => {

       
    desktopStream = await navigator.getDisplayMedia({video:true});
    webcamStream = await navigator.mediaDevices.getUserMedia({video: { height: 1080, width: 1920 }, audio: true});
    
    // Always 
    let tracks = [...desktopStream.getTracks(), ... webcamStream.getTracks()]
    console.log('Tracks to add to stream', tracks);
    stream = new MediaStream(tracks);
    
    console.log('Tracks on stream', stream.getTracks());
    
    videoElement.srcObject = stream;
    
    console.log('Tracks on video element that has stream', videoElement.videoTracks)
    
    // I would expect the length to be 2 and not 1
  };

};

Building a video editor on the web. Part 0.

Paul Kinlan

Anda harus dapat membuat dan mengedit video hanya dengan menggunakan web di browser. Seharusnya mungkin untuk menyediakan antarmuka pengguna yang mirip dengan Screenflow yang memungkinkan Anda membuat video output yang menggabungkan beberapa video, gambar, dan audio ke dalam satu video yang dapat diunggah ke layanan seperti YouTube. Posting ini benar-benar hanya pernyataan niat. Saya akan memulai proses panjang untuk mencari tahu apa yang ada dan tidak tersedia di peron dan melihat sejauh mana kita bisa mendapatkan hari ini.

Read More

Barcode detection in a Web Worker using Comlink

Paul Kinlan

Saya penggemar berat QRCodes, mereka sangat sederhana dan rapi untuk bertukar data antara dunia nyata dan dunia digital. Selama beberapa tahun, saya punya proyek sampingan kecil bernama QRSnapper & mdash; baik itu memiliki beberapa nama, tapi ini adalah salah satu yang telah saya selesaikan & mdash; yang menggunakan API getUserMedia untuk mengambil data langsung dari kamera pengguna sehingga dapat memindai QR Codes dalam waktu dekat.

Tujuan dari aplikasi ini adalah untuk mempertahankan 60fps di UI dan dekat deteksi instan dari Kode QR, ini berarti bahwa saya harus memasukkan kode pendeteksian ke dalam Web Worker (barang standar yang lumayan). Dalam posting ini saya hanya ingin cepat berbagi bagaimana saya menggunakan comlink untuk menyederhanakan logika secara besar-besaran dalam Pekerja.

qrclient.js

import * as Comlink from './comlink.js';

const proxy = Comlink.proxy(new Worker('/scripts/qrworker.js')); 

export const decode = async function (context) {
  try {
    let canvas = context.canvas;
    let width = canvas.width;
    let height = canvas.height;
    let imageData = context.getImageData(0, 0, width, height);
    return await proxy.detectUrl(width, height, imageData);
  } catch (err) {
    console.log(err);
  }
};

qrworker.js (pekerja web)

import * as Comlink from './comlink.js';
import {qrcode} from './qrcode.js';

// Use the native API's
let nativeDetector = async (width, height, imageData) => {
  try {
    let barcodeDetector = new BarcodeDetector();
    let barcodes = await barcodeDetector.detect(imageData);
    // return the first barcode.
    if (barcodes.length > 0) {
      return barcodes[0].rawValue;
    }
  } catch(err) {
    detector = workerDetector;
  }
};

// Use the polyfil
let workerDetector = async (width, height, imageData) => {
  try {
    return qrcode.decode(width, height, imageData);
  } catch (err) {
    // the library throws an excpetion when there are no qrcodes.
    return;
  }
}

let detectUrl = async (width, height, imageData) => {
  return detector(width, height, imageData);
};

let detector = ('BarcodeDetector' in self) ? nativeDetector : workerDetector;
// Expose the API to the client pages.
Comlink.expose({detectUrl}, self);

Saya sangat menyukai Comlink, saya pikir ini adalah game changer dari sebuah perpustakaan terutama ketika membuat JavaScript idiomatik yang berfungsi di seluruh thread. Akhirnya hal yang rapi di sini, adalah bahwa API deteksi Barcode asli dapat dijalankan di dalam pekerja sehingga semua logika diringkas dari UI.

Baca pos lengkap.

Running FFMPEG with WASM in a Web Worker

Paul Kinlan

Saya suka FFMPEG.js, ini adalah alat yang rapi yang dikompilasi dengan asm.js`dan ini memungkinkan saya membangun aplikasi web JS yang dapat mengedit video dengan cepat. FFMPEG.js juga berfungsi dengan pekerja web sehingga Anda dapat menyandikan video tanpa memblokir utas utama.

Saya juga suka Comlink. Comlink mari saya dengan mudah berinteraksi dengan pekerja web dengan mengekspos fungsi dan kelas tanpa harus berurusan dengan mesin negara postMessage yang kompleks.

Saya baru saja menggabungkan keduanya bersama. Saya bereksperimen mendapatkan FFMPEG diekspor ke Web Assembly (berfungsi - yay) dan saya ingin membersihkan semua pekerjaan postMessage dalam proyek FFMPEG.js saat ini. Di bawah ini adalah apa kode sekarang terlihat - saya pikir itu cukup rapi. Kami memiliki satu pekerja yang mengimpor ffmpeg.js dan comlink dan itu hanya memperlihatkan antarmuka ffmpeg, dan kemudian kami memiliki halaman web yang memuat pekerja dan kemudian menggunakan comlink untuk membuat proxy ke API ffmpeg.

Rapi.

worker.js

importScripts('https://cdn.jsdelivr.net/npm/comlinkjs@3.0.2/umd/comlink.js');
importScripts('../ffmpeg-webm.js'); 
Comlink.expose(ffmpegjs, self);

client.html

let ffmpegjs = await Comlink.proxy(worker);
let result = await ffmpegjs({
   arguments: ['-y','-i', file.name, 'output.webm'],
   MEMFS: [{name: file.name, data: data}],
   stdin: Comlink.proxyValue(() => {}),
   onfilesready: Comlink.proxyValue((e) => {
     let data = e.MEMFS[0].data;
     output.src = URL.createObjectURL(new Blob([data]))
     console.log('ready', e)
   }),
   print: Comlink.proxyValue(function(data) { console.log(data); stdout += data + "\n"; }),
   printErr: Comlink.proxyValue(function(data) { console.log('error', data); stderr += data + "\n"; }),
   postRun: Comlink.proxyValue(function(result) { console.log('DONE', result); }),
   onExit: Comlink.proxyValue(function(code) {
     console.log("Process exited with code " + code);
     console.log(stdout);
   }),
});

Saya sangat suka bagaimana modul Comlink, Workers dan WASM compiled dapat bermain bersama. Saya mendapatkan JavaScript idiomatik yang berinteraksi dengan modul WASM secara langsung dan menjalankan dari utas utama.

Baca pos lengkap.

Translating a blog using Google Cloud Translate and Hugo

Paul Kinlan

Baru-baru ini saya kembali dari perjalanan ke India untuk menghadiri acara Google4India (segera laporkan) dan untuk bertemu dengan banyak bisnis dan pengembang. Salah satu perubahan paling menarik yang dibahas adalah dorongan untuk lebih banyak konten dalam bahasa pengguna di negara tersebut, dan itu sangat jelas di semua produk Google yang berkisar dari membuatnya lebih mudah untuk mencari dalam bahasa pengguna, untuk menemukan konten, dan juga untuk membacanya kembali ke pengguna baik dalam bentuk teks atau suara.

Seluruh perjalanan membuatku berpikir. Blog saya dibangun dengan Hugo. Hugo sekarang mendukung konten secara tertulis dalam berbagai bahasa. Hugo sepenuhnya statis, sehingga membuat konten baru adalah masalah hanya membuat file baru dan membiarkan sistem build melakukan sihirnya. Jadi mungkin saya bisa membangun sesuatu yang akan membuat konten saya lebih tersedia bagi lebih banyak orang dengan menjalankan konten statis saya melalui alat terjemahan karena terjemahan konten manusia sangat mahal.

Beberapa jam sebelum penerbangan saya kembali ke Inggris saya membuat skrip kecil yang akan mengambil file penurunan harga saya dan menjalankannya melalui Google Cloud Translate untuk membuat terjemahan halaman yang kemudian dapat saya host dengan cepat. Seluruh solusi disajikan di bawah ini. Ini adalah prosesor yang relatif dasar, mengabaikan Hugo preamble itu mengabaikan ‘kode’ dan mengabaikan tanda kutip - asumsi saya adalah bahwa ini selalu dimaksudkan untuk ditinggalkan seperti cara mereka ditulis.

Catatan: Sepertinya perangkat lunak pembelajaran kami untuk terjemahan menggunakan sehingga penting untuk menandai halaman Anda sehingga alat pembelajaran tidak menggunakan konten Google Terjemahan sebagai masukan ke algoritme ita.

// Imports the Google Cloud client library
const Translate = require('@google-cloud/translate');
const program = require('commander');
const fs = require('fs');
const path = require('path');

program
  .version('0.1.0')
  .option('-s, --source [path]', 'Add in the source file.')
  .option('-t, --target [lang]', 'Add target language.')
  .parse(process.argv);

// Creates a client
const translate = new Translate({
  projectId: 'html5rocks-hrd'
});

const options = {
  to:  program.target,
};

async function translateLines(text) {
  if(text === ' ') return ' ';
  const output = [];
  let results = await translate.translate(text, options);

  let translations = results[0];
  translations = Array.isArray(translations)
    ? translations
    : [translations];

  translations.forEach((translation, i) => {
    output.push(translation)
  });

  return output.join('\n');
};

// Translates the text into the target language. "text" can be a string for
// translating a single piece of text, or an array of strings for translating
// multiple texts.
(async function (filePath, target) {

  const text = fs.readFileSync(filePath, 'utf8');

  const lines = text.split('\n');
  let translateBlock = [];
  const output = [];

  let inHeader = false;
  let inCode = false;
  let inQuote = false;
  for (const line of lines) {
    // Don't translate preampble
    if (line.startsWith('---') && inHeader) { inHeader = false; output.push(line); continue; }
    if (line.startsWith('---')) { inHeader = true; output.push(line); continue; }
    if (inHeader) { output.push(line); continue; }

    // Don't translate code
    if (line.startsWith('```') && inCode) { inCode = false; output.push(line); continue; }
    if (line.startsWith('```')) { inCode = true; output.push(await translateLines(translateBlock.join(' '))); translateBlock = []; output.push(line); continue; }
    if (inCode) { output.push(line); continue; }

    // Dont translate quotes
    if (inQuote && line.startsWith('>') === false) { inQuote = false; }
    if (line.startsWith('>')) { inQuote = true; output.push(await translateLines(translateBlock.join(' '))); translateBlock = []; output.push(line); }
    if (inQuote) { output.push(line); continue; }

    if (line.charAt(0) === '\n' || line.length === 0) { output.push(await translateLines(translateBlock.join(' '))); output.push(line); translateBlock = []; continue;} 

    translateBlock.push(line);
  }

  if(translateBlock.length > 0) output.push(await translateLines(translateBlock.join(' ')))

  const result = output.join('\n');
  const newFileName = path.parse(filePath);
  fs.writeFileSync(`content/${newFileName.name}.${target}${newFileName.ext}`, result);

})(program.source, program.target);

Secara keseluruhan, saya sangat senang dengan prosesnya. Saya memahami bahwa terjemahan mesin tidak sempurna tetapi pemikiran saya adalah bahwa saya dapat meningkatkan jangkauan konten saya kepada orang-orang yang mungkin mencari dalam bahasa mereka sendiri dan tidak dalam bahasa Inggris saya dapat meningkatkan luas permukaan penemuan konten saya dan semoga membantu lebih banyak orang-orang.

Ini akan memakan waktu cukup lama untuk melihat apakah ini benar-benar membantu orang, jadi saya akan melaporkan kembali ketika saya memiliki lebih banyak data …. Sekarang untuk menjalankan skrip saya di lebih banyak situs saya :)

Apple - Web apps - All Categories

Paul Kinlan

Ingat ketika Aplikasi Web * a * cara yang disarankan untuk menggunakan aplikasi di iPhone?

What are web apps? Learn what they are and how to use them.

Baca posting lengkap.

Pada sekitar 2013 Apple mulai mengarahkan direktori / webapps / top-level ke / iphone /

Masalahnya, direktori itu sebenarnya cukup bagus, banyak aplikasi di sana masih berfungsi hari ini. Namun melihat AppStore itu memecahkan lebih banyak masalah yang pengembang miliki: Penemuan yang lebih baik dan pencarian khusus karena AppStore langsung di perangkat. AppStore juga mulai memperkenalkan bahwa penghapusan friksi dari pengguna dan pengembang secara khusus berkaitan dengan pembayaran.

Gears API

Paul Kinlan

Saya sedang menulis posting blog tentang Mobile Web API awal dan Alex Russell mengingatkan saya tentang Google Gears

Gears modules include:

  • LocalServer Cache and serve application resources (HTML, JavaScript, images, etc.) locally
  • Database Store data locally in a fully-searchable relational database
  • WorkerPool Make your web applications more responsive by performing resource-intensive operations asynchronously

Baca pos lengkap.

Saya pikir itu menarik untuk melihat bahwa AppCache dan WebSQL, Geolocation dan WebWorkers keluar dari ide di Google Gears dan hanya dua yang terakhir yang benar-benar selamat. WebSQL tidak pernah didukung secara luas, dan digantikan oleh IndexedDB; dan AppCache digantikan oleh ServiceWorker

RSS Feed to Google Chat Webhook using Cloud Functions for Firebase and Superfeedr

Paul Kinlan

Kami menggunakan Google Chat secara internal untuk berkomunikasi di seluruh tim kami - ini seperti kendur kami; Kami juga membuat banyak konten yang dapat diakses melalui umpan RSS, kami bahkan memiliki umpan tim yang dapat Anda semua lihat. Baru-baru ini saya menemukan bahwa cukup mudah untuk membuat bot pasca-hanya yang sederhana melalui WebHooks dan itu memberi saya ide, saya dapat membuat layanan sederhana yang mengumpulkan umpan RSS dan kemudian mengirimnya ke webhook kami yang dapat dikirim langsung ke obrolan tim kami.

Itu sangat sederhana pada akhirnya, dan saya sudah memasukkan semua kode di bawah ini. Saya menggunakan fungsi Firebase - saya curiga bahwa ini sama mudahnya di situs fungsi-as-a-service lainnya - dan Superfeedr. Superfeedr adalah layanan yang dapat mendengarkan ping Pubsubhubbub (sekarang WebSub) dan ini juga akan mensurvei umpan RSS yang tidak memiliki Pubsub diatur. Kemudian ketika menemukan umpan, ia akan melakukan ping ke URL yang dikonfigurasi (dalam kasus saya, Cloud Function saya di Firebase) dengan representasi XML atau JSON dari data feed yang baru ditemukan - yang harus Anda lakukan hanyalah menguraikan data dan melakukan sesuatu dengannya.

const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const app = express();

// Automatically allow cross-origin requests
app.use(cors({ origin: true }));

app.post('/', (req, res) => {
  const { webhook_url } = req.query;
  const { body } = req;
  if (body.items === undefined || body.items.length === 0) {
    res.send('');
    return;
  }

  const item = body.items[0];
  const actor = (item.actor && item.actor.displayName) ? item.actor.displayName : body.title;

  fetch(webhook_url, {
    method: 'POST',
    headers: {
      "Content-Type": "application/json; charset=utf-8",
    },
    body: JSON.stringify({
      "text": `*${actor}* published <${item.permalinkUrl}|${item.title}>. Please consider <https://twitter.com/intent/tweet?url=${encodeURIComponent(body.items[0].permalinkUrl)}&text=${encodeURIComponent(body.items[0].title)}|Sharing it>.`
    })  
  }).then(() => {
    return res.send('ok');
  }).catch(() => {
    return res.send('error')
  });
})
// Expose Express API as a single Cloud Function:
exports.publish = functions.https.onRequest(app);

Baca posting lengkap.

Saya terkejut dan senang tentang betapa mudahnya mengaturnya.

Using HTTPArchive and Chrome UX report to get Lighthouse score for top visited sites in India.

Paul Kinlan

A quick dive in to how to use Lighthouse,HTTPArchive and Chrome UX report to try and understand how users in a country might experience the web.

Read More